﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Wyse.Server.Common
{
    /// <summary>
    /// Circular buffer generic class
    /// </summary>
    public class CircularBuffer<T>
    {
        /// <summary>
        /// Creates the buffer and sets it's capacity
        /// </summary>
        /// <param name="capacity">
        /// specifies the number of elements the buffer
        /// will at least be able to hold - the actual capacity may be larger
        /// </param>
        public CircularBuffer(int capacityHint)
        {
            int cap = RoundUp(capacityHint);

            mBuffer = new T[cap];
        }

        /// <summary>
        /// Gets the buffer's capacity
        /// </summary>
        public int Capacity
        {
            get { return mBuffer.Length; }
        }

        /// <summary>
        /// Gets the buffer's current size
        /// </summary>
        public int Size
        {
            get { return mCount; }
        }

        /// <summary>
        /// Adds a single item to the buffer
        /// </summary>
        public void Push(T item)
        {
            int cap = mBuffer.Length;

            if (mCount + 1 >= cap)
                throw new OverflowException();

            int p = (mHead + mCount) & (cap - 1);
            mBuffer[p] = item;
            ++mCount;
        }

        /// <summary>
        /// Adds an array of items to the buffer
        /// </summary>
        public void Push(T[] items, int off, int count)
        {
            int cap = mBuffer.Length;

            if (mCount + count >= cap)
                throw new OverflowException();
            int p = (mHead + mCount) & (cap - 1);
            int toCopy;

            // copy first part of the buffer
            toCopy = Math.Min(cap - p, count);
            Array.Copy(items, off, mBuffer, p, toCopy);

            if (toCopy - count > 0)
            {
                // copy second part
                Array.Copy(items, toCopy, mBuffer, 0, toCopy - count);
            }

            mCount += count;
        }

        /// <summary>
        /// Removes a single item from the buffer
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            if (mCount <= 0)
                throw new InvalidOperationException();
            T v = mBuffer[mHead];
            mHead = (mHead + 1) & (mBuffer.Length - 1);

            return v;
        }

        /// <summary>
        /// Removes a number of bytes from the buffer
        /// </summary>
        public void Pop(T[] items, int off, int count)
        {
            if (mCount < count)
                throw new InvalidOperationException();

            int cap = mBuffer.Length;

            // copy first part of buffer
            int toCopy = Math.Min(cap - mHead, count);
            Array.Copy(mBuffer, mHead, items, off, toCopy);

            if (toCopy - count > 0)
            {
                // copy second part
                Array.Copy(mBuffer, 0, items, toCopy, toCopy - count);
            }

            mHead = (mHead + count) & (mBuffer.Length - 1);
        }

        /// <summary>
        /// Rounds v to the neares power of 2
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private static int RoundUp(int v)
        {
            // from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2

            v--;
            v |= v >> 1;
            v |= v >> 2;
            v |= v >> 4;
            v |= v >> 8;
            v |= v >> 16;
            v++;

            return v;
        }

        private int mHead, mCount;
        private T[] mBuffer;
    }
}
